from asgiref.sync import sync_to_async
from fastapi import FastAPI, HTTPException, Request
from fastapi.exceptions import RequestValidationError
from pydantic import ValidationError
from uvicorn.protocols.http.h11_impl import STATUS_PHRASES

from app.common.exception.errors import BaseExceptionMixin
from app.common.response.response_code import CustomResponseCode, StandardResponseCode
from app.schemas.base import CUSTOM_VALIDATION_ERROR_MESSAGES
from app.utils.serializers import MsgSpecJSONResponse
from app.common.response.response_schema import response_base


@sync_to_async()
def _get_exception_code(status_code: int):
    try:
        code = STATUS_PHRASES[status_code]
    except Exception:
        code = StandardResponseCode.HTTP_400
    else:
        code = status_code
    return code


async def _validation_exception_handler(
    request: Request, e: RequestValidationError | ValidationError
):
    errors = []
    for error in e.errors():
        custom_message = CUSTOM_VALIDATION_ERROR_MESSAGES.get(error["type"])
        if custom_message:
            ctx = error.get("ctx")
            if not ctx:
                error["msg"] = custom_message
            else:
                error["msg"] = custom_message.format(**ctx)
                ctx_error = ctx.get("error")
                if ctx_error:
                    error["ctx"]["error"] = (
                        ctx_error.__str__().replace("'", '"')
                        if isinstance(ctx_error, Exception)
                        else None,
                    )
        errors.append(error)
    error = errors[0]
    if error.get("type") == "json_invalid":
        message = "解析失败"
    else:
        error_input = error.get("input")
        field = str(error.get("loc")[-1])
        error_msg = error.get("msg")
        message = error_msg
    msg = f"请求参数非法：{message}"
    data = {"errors": errors}
    content = {
        "code": StandardResponseCode.HTTP_422,
        "msg": msg,
        "data": data,
    }
    request.state.__request_validation_exception__ = content
    return MsgSpecJSONResponse(status_code=422, content=content)


def register_excetpion_handler(app: FastAPI):
    @app.exception_handler(HTTPException)
    async def http_exception_handler(request: Request, exc: HTTPException):
        res = await response_base.fail(res=CustomResponseCode.HTTP_400)
        content = res.model_dump()
        request.state.__request_http_exception__ = content
        return MsgSpecJSONResponse(
            status_code=await _get_exception_code(exc.status_code),
            content=content,
            headers=exc.headers,
        )

    @app.exception_handler(RequestValidationError)
    async def fastapi_validation_exception_handler(
        request: Request, exc: RequestValidationError
    ):
        return await _validation_exception_handler(request, exc)

    @app.exception_handler(Exception)
    async def all_exception_handler(request: Request, exc: Exception):
        if isinstance(exc, BaseExceptionMixin):
            return MsgSpecJSONResponse(
                status_code=await _get_exception_code(exc.code),
                content={
                    "code": exc.code,
                    "msg": str(exc.msg),
                    "data": exc.data if exc.data else None,
                },
                background=exc.background,
            )
        else:
            import traceback

            res = await response_base.fail(res=CustomResponseCode.HTTP_500)
            content = res.model_dump()

        return MsgSpecJSONResponse(
            status_code=StandardResponseCode.HTTP_500,
            content=content,
        )
